//===============================================================================
// Microsoft patterns & practices Enterprise Library Contribution
// Resource Application Block
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Instrumentation;
using Microsoft.Practices.ObjectBuilder;
using EntLibContrib.Resource.Configuration;
using EntLibContrib.Resource.Instrumentation;
using EntLibContrib.Resource.Managers;

namespace EntLibContrib.Resource
{
	/// <summary>
	/// This type supports the Enterprise Library infrastructure and is not intended to be used directly from your code.
	/// Represents the process used to build an instance of <see cref="ResourceManagerData"/> described by the <see cref="ResourceManagerSettings"/> configuration section.
	/// </summary>
	/// <remarks>
	/// This is used by the <see cref="ConfiguredObjectStrategy"/> when an instance of the <see cref="ResourceManagerData"/> class is requested to 
	/// a properly configured <see cref="IBuilder{TStageEnum}"/> instance.
	/// </remarks>
	public class ResourceManagerCustomFactory : ICustomFactory
	{
		#region Public Methods
		/// <summary>
		/// This method supports the Enterprise Library infrastructure and is not intended to be used directly from your code.
		/// Builds a <see cref="ResourceManagerData"/> described by the <see cref="ResourceManagerSettings"/> configuration section.
		/// </summary>
		/// <param name="context">The <see cref="IBuilderContext"/> that represents the current building process.</param>
		/// <param name="name">The name of the instance to build. It is part of the <see cref="ICustomFactory.CreateObject(IBuilderContext, string, IConfigurationSource, ConfigurationReflectionCache)"/> method, but it is not used in this implementation.</param>
		/// <param name="configurationSource">The source for configuration objects.</param>
		/// <param name="reflectionCache">The cache to use retrieving reflection information.</param>
		/// <returns>
		/// A fully initialized instance of <see cref="ResourceManager"/>.
		/// </returns>
		public object CreateObject (IBuilderContext context, string name, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
		{
			ResourceManagerData objectConfiguration = GetConfiguration(name, configurationSource);

			IResourceProvider resourceProvider = ResourceProviderCustomFactory.Instance.Create(context, objectConfiguration.ResourceProvider, configurationSource, reflectionCache);

			ResourceInstrumentationProvider instrumentationProvider = CreateInstrumentationProvider(objectConfiguration.Name, configurationSource, reflectionCache);

			ResourceManager createdObject = new ResourceManagerFactoryHelper().BuildResourceManager(objectConfiguration.Name, resourceProvider,	instrumentationProvider);

			RegisterObject(context, name, createdObject);

			return createdObject;
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Creates the instrumentation provider.
		/// </summary>
		/// <param name="name">The instance name.</param>
		/// <param name="configurationSource">The configuration source.</param>
		/// <param name="reflectionCache">The reflection cache.</param>
		/// <returns></returns>
		protected static ResourceInstrumentationProvider CreateInstrumentationProvider (string name, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
		{
			ResourceInstrumentationProvider instrumentationProvider = new ResourceInstrumentationProvider();
			ResourceInstrumentationListener instrumentationListener = CreateInstrumentationListener(name, configurationSource, reflectionCache);
			if (instrumentationListener.EventLoggingEnabled || instrumentationListener.PerformanceCountersEnabled || instrumentationListener.WmiEnabled)
			{
				ReflectionInstrumentationBinder binder = new ReflectionInstrumentationBinder();
				binder.Bind(instrumentationProvider, instrumentationListener);
			}

			return instrumentationProvider;
		}

		/// <summary>
		/// Creates the instrumentation listener.
		/// </summary>
		/// <param name="name">The instance name.</param>
		/// <param name="configurationSource">The configuration source.</param>
		/// <param name="reflectionCache">The reflection cache.</param>
		/// <returns></returns>
		protected static ResourceInstrumentationListener CreateInstrumentationListener(string name, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache)
		{
			ResourceInstrumentationListenerCustomFactory instrumentationListenerFactory = new ResourceInstrumentationListenerCustomFactory();
			return (ResourceInstrumentationListener)instrumentationListenerFactory.CreateObject(null, name, configurationSource, reflectionCache);
		}

		/// <summary>
		/// Gets the configuration.
		/// </summary>
		/// <param name="id">The id.</param>
		/// <param name="configurationSource">The configuration source.</param>
		/// <returns></returns>
		protected ResourceManagerData GetConfiguration (string id, IConfigurationSource configurationSource)
		{
			ResourceConfigurationView view = new ResourceConfigurationView(configurationSource);
			return view.GetResourceManagerData(id);
		}

		/// <summary>
		/// Registers the object.
		/// </summary>
		/// <param name="context">The context.</param>
		/// <param name="name">The name.</param>
		/// <param name="createdObject">The created object.</param>
		private void RegisterObject (IBuilderContext context, string name, ResourceManager createdObject)
		{
			if ( context.Locator != null )
			{
				ILifetimeContainer lifetime = context.Locator.Get<ILifetimeContainer>(typeof(ILifetimeContainer), SearchMode.Local);

				if ( lifetime != null )
				{
					context.Locator.Add(new DependencyResolutionLocatorKey(typeof(ResourceManager), name), createdObject);
					lifetime.Add(createdObject);
				}
			}
		}
		#endregion
	}

	/// <summary>
	/// Public for testing purposes
	/// </summary>
	internal class ResourceManagerFactoryHelper
	{
		#region Public Methods
		/// <summary>
		/// Made public for testing purposes.
		/// </summary>
		/// <param name="resourceManagerName">Name of the resource manager.</param>
		/// <param name="resourceProvider">The resource provider.</param>
		/// <param name="instrumentationProvider">The instrumentation provider.</param>
		/// <returns></returns>
		public ResourceManager BuildResourceManager (string resourceManagerName, IResourceProvider resourceProvider, ResourceInstrumentationProvider instrumentationProvider)
		{
			ExtendedComponentResourceManager resourceManager = resourceProvider.CreateResourceManager();
			resourceManager.InstrumentationProvider = instrumentationProvider;
			return new ResourceManager(resourceManagerName, resourceManager);
		}
		#endregion
	}
}